<?php

declare(strict_types=1);

namespace App\Helpers;

use App\Cache\RedisKey;
use App\Constants\UserUpdateProfileCode;
use Hyperf\Redis\RedisFactory;
use \Psr\SimpleCache\CacheInterface;
use Redis;
use RedisException;

class RedisUtil
{
    //websocket客户端fd的缓存key
    const WS_CLIENT_FD_KEY = 'websocket_sjd_1';

    public static function getRedis($poolName = 'default')
    {
        $redis = \Hyperf\Utils\ApplicationContext::getContainer()->get(RedisFactory::class)->get($poolName);
        return $redis;
    }

    public static function connection($poolName)
    {
        return static::getRedis($poolName);
    }

    public static function get(string $key, \Closure $func = null, $ttl = 60, $poolName = 'cache', $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        $redis = static::getRedis($poolName);
        if (!($data = $redis->get($key)) && $func) {
            $redis->set($key, $data = $func(), $ttl);
        }
        return $data;
    }

    public static function set(string $key, $value, $ttl = 90, $poolName = 'cache', $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis($poolName)->set($key, $value, $ttl);
    }

    public static function delete(string $key, $poolName = 'cache', $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis($poolName)->del($key);
    }

    public static function hget(string $key, string $keyValue, $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis('cache')->hget($key, $keyValue);
    }

    public static function hset(string $key, string $keyValue, $value = '', $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis('cache')->hset($key, $keyValue, $value);
    }

    public static function rPop(string $key, $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis('cache')->rPop($key);
    }

    public static function lPush(string $key, $value = '', $prefix = null)
    {
        if ($prefix === null) $key = config('cache.default.prefix') . $key;
        return static::getRedis('cache')->lPush($key, $value);
    }

    /** 并发锁 (true 表示获得锁)
     * @param $key
     * @param int $ttl //多少秒内只允许执行一次
     * @return bool
     * @throws \Exception
     */
    public static function concurrentLock($key, $ttl = 3, $poolName = 'cache')
    {
        try {
            $ok = static::getRedis($poolName)->get('__RedisLock__' . $key);
            if ($ok) {
                return true;
            } else {
                $random = rand(1, 999);
                static::getRedis($poolName)->set('__RedisLock__' . $key, $random, $ttl);
                return false;
            }
        } catch (\Exception $e) {
            return false;
        }
    }


    /**
     * 原子加锁
     * @param $key
     * @param int $ttl
     * @param string $poolName
     * @return mixed|Redis
     */
    public static function addLock($key, int $ttl = 3, string $poolName = 'cache')
    {
        $script = <<<lua
            local isLock = redis.call('exists',KEYS[1])
            if isLock == 1 then
                return 0
            else
                redis.call('set',KEYS[1],1)
                redis.call('Expire',KEYS[1],ARGV[1])
                return 1;
            end
        lua;

        return static::getRedis($poolName)->eval($script, [$key, $ttl], 1);
    }

    /**
     * 解锁
     * @param $key
     * @param string $poolName
     * @return void
     */
    public static function delLock($key, string $poolName = 'cache')
    {
        static::getRedis($poolName)->del($key);
    }

    /**
     * 增加更新用户昵称/恋爱宣言
     * @param int $userId
     * @param string $type
     * @param string $poolName
     * @return mixed|Redis
     * @throws RedisException
     */
    public static function addUpdateProfileLock(int $userId, string $type = 'nickname', string $poolName = 'cache')
    {
        $script = <<<lua
            local isLock = redis.call('exists', KEYS[1])
            if isLock == 1 then
                local total = redis.call('get', KEYS[1])
                if total >= ARGV[2] then
                    return 1
                else 
                    redis.call('INCRBY', KEYS[1], 1);
                    return 0
                end
            else
                redis.call('set', KEYS[1], 1)
                redis.call('Expire', KEYS[1], ARGV[1])
                return 0
            end
        lua;

        $curTime = time();
        $tomorrowTime = strtotime(date('Y-m-d 00:00:00', strtotime('+1 day')));

        $ttl = $tomorrowTime - $curTime;

        $key = RedisKey::operationUserProfileByNicknameLock($userId);
        $maxLimit = UserUpdateProfileCode::UPDATE_NICKNAME_MAX;
        if ($type == 'intro') {
            $key = RedisKey::operationUserProfileByIntroLock($userId);
            $maxLimit = UserUpdateProfileCode::UPDATE_INTRO_MAX;
        }

        return static::getRedis($poolName)->eval($script, [$key, $ttl, $maxLimit], 1);
    }
}
